package dao

import (
	"context"
	"database/sql"
	"errors"
	"github.com/TimeWtr/tinylink/interval/domain"
	"github.com/go-sql-driver/mysql"
	"gorm.io/gorm"
	"gorm.io/plugin/dbresolver"
	"time"
)

type URLRecordsInter interface {
	Insert(ctx context.Context, url domain.URL) (URLRecords, error)
	Update(ctx context.Context, url domain.URL) (URLRecords, error)
	Delete(ctx context.Context, id int64) error
	FindByShortURL(ctx context.Context, shortUrl string) (URLRecords, error)
}

var _ URLRecordsInter = (*URLRecordsDao)(nil)

var ErrURLConflict = errors.New("url conflict")

type URLRecordsDao struct {
	db *gorm.DB
}

func NewURLRecordsDao(db *gorm.DB) URLRecordsInter {
	return &URLRecordsDao{db: db}
}

func (d *URLRecordsDao) Migrator() error {
	return d.db.AutoMigrate(&URLRecords{})
}

func (d *URLRecordsDao) Insert(ctx context.Context, url domain.URL) (URLRecords, error) {
	err := d.db.WithContext(ctx).Clauses(dbresolver.Write).
		Model(&URLRecords{}).Create(&URLRecords{
		Biz:        url.Biz,
		LongURL:    url.LongURL,
		ShortURL:   url.ShortURL,
		Period:     url.Period,
		CreateTime: time.Now().UnixMilli(),
		UpdateTime: sql.NullInt64{},
	}).Error
	if err != nil {
		var mysqlErr *mysql.MySQLError
		if errors.As(err, &mysqlErr) {
			const uniqueConflictsError uint16 = 1062
			if mysqlErr.Number == uniqueConflictsError {
				return URLRecords{}, ErrURLConflict
			}
		}

		return URLRecords{}, err
	}

	var result URLRecords
	return result, d.db.WithContext(ctx).Clauses(dbresolver.Write).
		Model(&URLRecords{}).Where("long_url = ?", url.LongURL).
		First(&result).Error
}

func (d *URLRecordsDao) Update(ctx context.Context, url domain.URL) (URLRecords, error) {
	err := d.db.WithContext(ctx).Clauses(dbresolver.Write).
		Where("id = ?", url.LongURL).
		Updates(map[string]any{
			"short_url": url.ShortURL,
			"update_time": sql.NullInt64{
				Int64: time.Now().UnixMilli(),
				Valid: true,
			},
		}).Error
	if err != nil {
		return URLRecords{}, err
	}

	var result URLRecords
	return result, d.db.WithContext(ctx).Clauses(dbresolver.Write).
		Model(&URLRecords{}).Where("id = ?", url.ID).
		First(&result).Error
}

func (d *URLRecordsDao) Delete(ctx context.Context, id int64) error {
	return d.db.WithContext(ctx).Clauses(dbresolver.Write).
		Where("id = ?", id).
		Delete(&URLRecords{}).Error
}

func (d *URLRecordsDao) FindByShortURL(ctx context.Context, shortUrl string) (URLRecords, error) {
	var url URLRecords
	return url, d.db.WithContext(ctx).Clauses(dbresolver.Read).
		Where("short_url = ?", shortUrl).
		First(&url).Error
}

type URLRecords struct {
	ID         int64         `gorm:"column:id;type:bigint;primaryKey;not null;comment:主键" json:"id"`
	Biz        string        `gorm:"column:biz;type:biz;not null;comment:业务" json:"biz"`
	LongURL    string        `gorm:"column:long_url;type:text;not null;uniqueIndex:idx_short_url;comment:长链接" json:"long_url"`
	ShortURL   string        `gorm:"column:short_url;type:varchar(255);uniqueIndex:idx_short_url;comment:短链接" json:"short_url"`
	Period     int           `gorm:"column:period;type:int;not null;default:0;comment:短链接的有效期" json:"period"`
	CreateTime int64         `gorm:"column:create_time;type:int;not null;comment:创建时间" json:"create_time"`
	UpdateTime sql.NullInt64 `gorm:"column:update_time;type:int;not null;comment:更新时间" json:"update_time"`
}

func (ur *URLRecords) TableName() string {
	return "url_records"
}
